import {
    _decorator,
    Component,
    Node,
    Label,
    assetManager,
    resources,
    Asset,
    JsonAsset,
    TextAsset,
    Prefab,
    instantiate,
    game,
    Scene,
    director,
    ProgressBar,
    UITransform,
    native,
} from "cc";
const { ccclass, property } = _decorator;

@ccclass("HotUpdate")
export class HotUpdate extends Component {
    @property({
        type: ProgressBar,
        displayName: "更新进度条",
    })
    updateBar!: ProgressBar;

    @property({
        type: Label,
        displayName: "当前信息",
    })
    infoLab!: Label;

    @property(Asset)
    manifestUrl!: Asset;

    @property(Asset)
    assetsManifestUrl!: Asset;

    PROJECT_MANIFEST_NAME = "project.manifest";

    REMOTE_PACAKGE_URL = "";
    NativeWritablePath = "";

    totalDownCount = 1;
    loadedCount = 0;

    onLoad() {
        this.NativeWritablePath = native.fileUtils.getWritablePath();
        const bar: Node = this.updateBar.node.getChildByName("Bar")!;
        bar.getComponent(UITransform)!.width = this.updateBar.node.getComponent(UITransform)!.width;
    }

    start() {
        this.checkUpdate();
    }

    httpGetPromise = (url: string): Promise<any> => {
        return new Promise((resolve, reject) => {
            let xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 400) {
                    let responseText = JSON.parse(xhr.responseText);
                    console.log("http status:", xhr.status);
                    if (xhr.status == 200) {
                        resolve(responseText);
                    } else {
                        reject();
                    }
                }
            };
            xhr.onerror = function () {
                reject();
            };
            xhr.open("GET", url, true);
            xhr.timeout = 5000; // 5 seconds for timeout
            xhr.send();
        });
    };

    httpGetBinary = (url: string): Promise<any> => {
        return new Promise((resolve, reject) => {
            let xhr = new XMLHttpRequest();
            xhr.onreadystatechange = () => {
                if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 400) {
                    console.log("http status:", xhr.status);
                    if (xhr.status == 200) {
                        this.loadedCount++;
                        resolve(xhr.response);
                    } else {
                        reject();
                    }
                }
            };
            xhr.onerror = function () {
                reject();
            };
            xhr.responseType = "arraybuffer";
            xhr.open("GET", url, true);
            xhr.timeout = 5000; // 5 seconds for timeout
            xhr.send();
        });
    };

    assetLoadTextPromise = (url: string): Promise<any> => {
        return new Promise((resolve, reject) => {
            assetManager.loadRemote(url, { ext: ".txt" }, (err, data) => {
                if (err) {
                    console.log("errL", err);
                    reject(err);
                } else {
                    resolve(data.toString());
                }
            });
        });
    };

    assetLoadBinaryPromise = (url: string): Promise<any> => {
        return new Promise((resolve, reject) => {
            assetManager.parser.register(".binary", (file, options, onComplete) => {
                console.log("file :", file);
            });
            assetManager.loadRemote(url, { ext: ".binary" }, (err, data) => {
                if (err) {
                    console.log("errL", err);
                    reject(err);
                } else {
                    console.log("data w", data);
                    resolve(data);
                }
            });
        });
    };

    private compareVersionAssets(localAssets: any, remoteAssets: any) {
        const downList = [];
        for (const key in remoteAssets) {
            const assetItem = localAssets[key];
            const remoteAssetItem = remoteAssets[key];
            if (remoteAssetItem) {
                if (assetItem) {
                    if (remoteAssetItem.md5 !== assetItem.md5) {
                        // modify
                        console.log("modify:", key);
                        downList.push(key);
                    }
                } else {
                    console.log("add:", key);
                    downList.push(key);
                }
            }
        }
        return downList;
    }

    private getLocalManifest(manifestName: string, nativeUrl: string) {
        let projectStr = native.fileUtils.getStringFromFile(this.NativeWritablePath + manifestName);
        console.log("projectStr:", manifestName, projectStr);
        if (!projectStr) {
            projectStr = native.fileUtils.getStringFromFile(nativeUrl);
            console.log("projectStr cache:", manifestName, projectStr);
        }
        return projectStr;
    }

    // 检测是否需要更新
    private async checkUpdate() {
        this.udpateShowInfo("检测更新");
        // 加载版本中的project.manifest
        const projectManifestStr = this.getLocalManifest("project.manifest", this.manifestUrl.nativeUrl);
        const localJsonData = JSON.parse(projectManifestStr);
        this.REMOTE_PACAKGE_URL = localJsonData.packageUrl;
        console.log("REMOTE_PACAKGE_URL", this.REMOTE_PACAKGE_URL);
        // 下载远程 project.manifest 对比版本
        const remoteJsonStr = await this.assetLoadTextPromise(
            this.REMOTE_PACAKGE_URL + "/" + this.PROJECT_MANIFEST_NAME
        );
        const remoteJsonData = await this.httpGetPromise(this.REMOTE_PACAKGE_URL + "/" + this.PROJECT_MANIFEST_NAME);
        console.log("remoteData", localJsonData.assetsVerson, remoteJsonData.assetsVerson);
        if (localJsonData.assetsVerson < remoteJsonData.assetsVerson) {
            const assetsJsonData = await this.httpGetPromise(
                `${this.REMOTE_PACAKGE_URL}/assets.${remoteJsonData.assetsVerson}.manifest`
            );
            const assetsManifestStr = this.getLocalManifest(
                `assets.${localJsonData.assetsVerson}.manifest`,
                this.assetsManifestUrl.nativeUrl
            );
            const localAssetsJsonData: any = JSON.parse(assetsManifestStr);
            console.log("localVersion:", assetsManifestStr);
            console.log("assetsJsonData:", JSON.stringify(assetsJsonData));
            const downList = this.compareVersionAssets(localAssetsJsonData.assets, assetsJsonData.assets);

            this.showPregressValue(0.2);
            this.loadedCount = 0;
            this.totalDownCount = downList.length;
            const downTaskList: Promise<any>[] = [];
            downList.forEach((item) => {
                downTaskList.push(this.httpGetBinary(this.REMOTE_PACAKGE_URL + "/" + item));
            });
            Promise.all(downTaskList).then((list) => {
                list.forEach((data, idx) => {
                    const writablePath = this.NativeWritablePath + "/" + downList[idx];
                    console.log("writablePath:", this.NativeWritablePath, writablePath);
                    const fileDir = native.fileUtils.getFileDir(writablePath);
                    if (!native.fileUtils.isDirectoryExist(fileDir)) {
                        native.fileUtils.createDirectory(fileDir);
                    }
                    if (native.fileUtils.isFileExist(writablePath)) {
                        native.fileUtils.removeFile(writablePath);
                    }
                    native.fileUtils.writeDataToFile(data, writablePath);
                });

                game.restart();
            });
        } else {
            director.loadScene("main");
        }
    }

    private udpateShowInfo(str: string) {
        this.infoLab.string = str;
    }

    private showPregressValue(val: number) {
        this.updateBar.progress = val;
        this.udpateShowInfo(`加载进度:${(val * 100).toFixed(2)}%`);
    }

    // 更新后界面
    showUpdatedPanel() {
        resources.load("test-prefab", Prefab, (err, prefab: Prefab) => {
            this.node.addChild(instantiate(prefab));
        });
    }

    switchScene() {
        director.loadScene("main");
    }

    update(deltaTime: number) {
        this.showPregressValue(Math.max(0.2, this.loadedCount / this.totalDownCount));
    }
}
